package main

import (
	"bufio"
	"fmt"
	os "os"
	"os/exec"
	io "io"

	flag "github.com/spf13/pflag"
)

var (
	start       = flag.IntP("start", "s", 0, "Number of starting page")
	end         = flag.IntP("end", "e", 0, "Number of ending page")
	length      = flag.IntP("length", "l", 72, "Number of lines each page displays")
	find        = flag.BoolP("find", "f", false, "If to find the page break")
	destination = flag.StringP("destination", "d", "", "Defines the destination of output")
	isSet       = make(map[string]bool)
)

func writeTo(ch string, output *os.File, cmdstdin io.WriteCloser) {
	if cmdstdin != nil {
		cmdstdin.Write([]byte(ch))
	} else {
		fmt.Fprint(output, ch)
	}
}

func readWrite(input *os.File, output *os.File, cmdstdin io.WriteCloser) {
	reader := bufio.NewReader(input)
	for i := 0; i < *end; i++ {
		if *find == true {
			for {
				ch, err := reader.ReadByte()
				if err != nil {
					break
				}
				if ch == '\f' {
					writeTo("\n", output, cmdstdin)
					break
				}
				if ch == '\n' || ch == '\r' {
					writeTo("\n", output, cmdstdin)
					continue
				}
				if i+1 >= *start {
					writeTo(string(ch), output, cmdstdin)
				}
			}
		} else {
			for j := 0; j < *length; j++ {
				for {
					ch, err := reader.ReadByte()
					if err != nil {
						break
					}
					if ch == '\n' || ch == '\r' || ch == '\f' {
						writeTo(string("\n"), output, cmdstdin)
						break
					}
					if i+1 >= *start {
						writeTo(string(ch), output, cmdstdin)
					}
				}
			}
		}
	}
	writeTo("\n", output, cmdstdin)
}

func setVisit() {
	flag.Visit(func(f *flag.Flag) {
		isSet[f.Name] = true
	})
}

func checkMandatory() {
	if isSet["start"] == false || isSet["end"] == false {
		fmt.Fprintf(os.Stderr, "missing	required argument(s) [-s, --start int] or [-e, -- end int]\n")
		os.Exit(2)
	}
	if isSet["find"] == true && isSet["length"] == true {
		fmt.Fprintf(os.Stderr, "arguments [-f, --find] and [-l, --length int] are exclusive, you can only set at most one of them\n")
		os.Exit(2)
	}
}

func inputArg() *os.File {
	if len(flag.Args()) == 1 {
		file, err := os.OpenFile(flag.Arg(0), os.O_RDONLY, 0666)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(2)
		}
		return file
	} else if len(flag.Args()) > 1 {
		fmt.Fprintln(os.Stderr, "Errors in format of inputfile")
		os.Exit(2)
	}
	return nil
}

func destinationArg() io.WriteCloser {
	if isSet["destination"] == true {
		lpcmd := exec.Command("lp", "-d"+*destination)
		lpstdin, err := lpcmd.StdinPipe()
		if(err != nil){
			fmt.Fprintln(os.Stderr, err)
			os.Exit(2)
		}
		lpcmd.Stdout = os.Stdout
		lpcmd.Stderr = os.Stderr
		lpcmd.Run()

		return lpstdin
	}
	return nil
}

func main() {
	flag.Parse()
	setVisit()
	checkMandatory()
	lpstdin := destinationArg()
	if file := inputArg(); file != nil {
		readWrite(file, os.Stdout, lpstdin)
	} else {
		readWrite(os.Stdin, os.Stdout, lpstdin)
	}
}
